package com.codingapi.websocket.core.resolver;

import com.sun.javafx.binding.StringFormatter;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author modificial
 * @date 2018/7/19
 * @company codingApi
 * @description 解析http和websocket请求
 */
public class UpgradeResolver {

    private static final Logger LOGGER= LoggerFactory.getLogger(UpgradeResolver.class);

    private int frameLength;

    /**
     * 处理http请求
     * @param ctx
     * @param request
     * @return
     */
    public WebSocketServerHandshaker handleRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
        try {
            boolean result;
            //处理升级请求
            result = handleUpgradeRequest(ctx, request);
            if (result) {
                WebSocketServerHandshaker handshaker = null;
                //握手成功
                if ((handshaker = doHandshake(ctx, request)) != null) {
                    return handshaker;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            LOGGER.error("处理请求失败");
            handleRequestError(ctx,e);
        }

        throw new RuntimeException("握手失败-----");
    }

    /**
     *
     * 处理异常请求
     *
     */
    public void handleRequestError(ChannelHandlerContext ctx, Throwable throwable) {
        DefaultFullHttpResponse defaultFullHttpResponse = new DefaultFullHttpResponse(
                HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_GATEWAY);

        ByteBuf buf = Unpooled.copiedBuffer(throwable.getMessage(),
                CharsetUtil.UTF_8);
        defaultFullHttpResponse.content().writeBytes(buf);
        buf.release();
        ChannelFuture f = ctx.channel().writeAndFlush(defaultFullHttpResponse);
        if (f.isSuccess()) {
            ctx.close();
        }
    }


    /**
     * 处理请求
     * @param ctx
     * @param request
     * @return
     */
    public boolean handleUpgradeRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
        //判断请求头
        if (!request.decoderResult().isSuccess()) {
            DefaultFullHttpResponse defaultFullHttpResponse = new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST);
            // 返回应答给客户端
            if (defaultFullHttpResponse.status().code() != 200) {
                ByteBuf buf = Unpooled.copiedBuffer(defaultFullHttpResponse.status().toString(),
                        CharsetUtil.UTF_8);
                defaultFullHttpResponse.content().writeBytes(buf);
                buf.release();
            }
            // 如果是非Keep-Alive，关闭连接
            ChannelFuture f = ctx.channel().writeAndFlush(defaultFullHttpResponse);

            if (defaultFullHttpResponse.status().code() != 200) {
                f.addListener(ChannelFutureListener.CLOSE);
            }
            return false;
        }
        return true;
    }

    /**
     * 握手连接
     */
    public WebSocketServerHandshaker doHandshake(ChannelHandlerContext ctx, FullHttpRequest request) {
        HttpHeaders httpHeaders = request.headers();
        String protocols = httpHeaders.get(WebSocketConstant.SEC_WEBSOCKET_PROTOCOL);
        String host = httpHeaders.get("Host");
        String uri = request.uri();
        String webAddress = StringFormatter.format(WebSocketConstant.DEFAULT_WEBSOCKET_ADDRESS_FORMAT, host).getValueSafe() + uri;

        //设置最大帧长度，保证安全
        if (frameLength == 0) {
            frameLength = 10 * 1024 * 1024;
        }
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
                webAddress, protocols, true, frameLength);

        WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(request);
        if (handshaker == null) {
            //版本不兼容
            WebSocketServerHandshakerFactory
                    .sendUnsupportedVersionResponse(ctx.channel());
        } else {
            handshaker.handshake(ctx.channel(), request);
        }
        return handshaker;
    }

    /**
     * 服务启动的必要参数
     */
    interface WebSocketConstant {
        String WEBSOCKET = "websocket";
        String Upgrade = "Upgrade";
        String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";
        String DEFAULT_WEBSOCKET_ADDRESS_FORMAT = "ws://%s";
    }
}
